クロスアカウントでSPAを自動デプロイする環境をCFnで構築する
はじめに
どうも、クラスメソッドの岡です。
今回アカウントをまたいでCodeCommitで管理しているAngular製SPAを継続的にデプロイする環境を作成するCFnテンプレートを作ってみました。
サーバーレスで作りやすいSPAですが、AWSで開発をする場合実際に稼働している環境と開発する環境はアカウントが分かれているパターンが多いかと思います。
また、ソースコードをCodeCommitで管理している場合はCodePipelineでデプロイフローを作るのが一般的ですがアカウントをまたぐ場合はIAMの設定等に気を使います。
ここでは、CloudFormationでAssumeRole元のIAMロールからCopePipeline、実際にS3にデプロイするCodeBuildプロジェクトまで一気に作成する方法をご紹介したいと思います。
シチュエーション
SPAの構成
まずデプロイ対象のSPAの構成は以下です。
デプロイ構成
- ソース管理は開発アカウントのCodeCommit
- ソースコードの環境の振り分けはブランチ名で
- アカウントをまたいでアクセスするアーティファクト用のS3バケットはKMSで暗号化
- Angularのデプロイ(S3に上書き)はCodeBuildのbuildファイルで設定
AngularをCodeBuildでデプロイする方法はこちらの記事でご紹介しています。
ここではビルドファイルをそれぞれ
- codebuild/buildspec.itg.yml
- codebuild/buildspec.stg.yml
- codebuild/buildspec.prd.yml
で作成済みとします。
CFnテンプレート
まずテンプレートを3つに分けました。 デプロイ先のアカウントとソースアカウントで作るリソースが変わるためです。
- デプロイ先のアカウントにpipeline,buildプロジェクトを作成するテンプレート
- ソースアカウントにAssumeRoleするためのIAMRoleを作成するテンプレート
- ソースアカウントにpipeline,buildプロジェクトを作成するテンプレート
デプロイ先のアカウント(ここではstagingかproduction)
ソースアカウント(ここでは開発アカウント)
デプロイ先のアカウントで実行するテンプレート
デプロイ先のアカウント、stagingとproductionで実行するテンプレートです。
作成するリソースは以下となります。
- CodePipelineのIAMRole
- CodebuildのIAMRole
- アーティファクトバケットの暗号化用のKMSキー
- アーティファクトバケット(S3)
- バケットポリシー
- CodePipelineのパイプライン
- CodeBuildのプロジェクト
AWSTemplateFormatVersion: '2010-09-09' Description: 'Create resources for automatic deployment of SPA' Parameters: Env: Type: String Default: stg AllowedValues: - stg - prd Description: 'Set Enter Environment for SPA' DeployAccountId: Type: String Default: 123456789012 AllowedValues: - 123456789012 - 098765432109 Description: 'Set Enter Account ID for SPA' SourceAccountId: Type: String Default: 678901234567 Description: 'Set Enter Account ID for SPA source' SpaName: Type: String Default: member-spa AllowedValues: - member-spa - qrcode-spa - management-console Description: 'Set Enter SPA name' BranchName: Type: String Default: staging AllowedValues: - staging - master Description: 'Set Enter BranchName for SPA' Resources: #pipeline用のIAMロールを作成 SpaPipelineRole: Type: AWS::IAM::Role Properties: AssumeRolePolicyDocument: Version: '2012-10-17' Statement: - Effect: Allow Principal: Service: codepipeline.amazonaws.com Action: sts:AssumeRole Policies: - PolicyDocument: Version: '2012-10-17' Statement: - Effect: Allow Action: - s3:* Resource: - !Sub arn:aws:s3:::${Env}-auto-deploy-${SpaName} - !Sub arn:aws:s3:::${Env}-auto-deploy-${SpaName}/* - Effect: Allow Action: - codebuild:StartBuild - codebuild:BatchGetBuilds Resource: - '*' - Effect: Allow Action: - sts:AssumeRole Resource: !Sub arn:aws:iam::${SourceAccountId}:role/${Env}-auto-deploy-${SpaName}-for-codecommit PolicyName: !Sub ${Env}-auto-deploy-${SpaName}-for-pipeline RoleName: !Sub ${Env}-auto-deploy-${SpaName}-for-pipeline #codebuild用のIAMロールを作成 SpaCodeBuildRole: Type: AWS::IAM::Role Properties: AssumeRolePolicyDocument: Version: '2012-10-17' Statement: - Effect: Allow Principal: Service: codebuild.amazonaws.com Action: sts:AssumeRole Policies: - PolicyDocument: Version: '2012-10-17' Statement: - Effect: Allow Action: - logs:* - cloudfront:CreateInvalidation Resource: - '*' - Effect: Allow Action: - s3:* Resource: - '*' PolicyName: !Sub ${Env}-auto-deploy-${SpaName}-for-codebuild RoleName: !Sub ${Env}-auto-deploy-${SpaName}-for-codebuild #S3暗号化の為のKMSキーを作成 SpaKmsKey: Type: AWS::KMS::Key Properties: Description: SPA deploy KeyPolicy: Version: '2012-10-17' Statement: - Sid: 'Enable IAM User Permissions' Effect: Allow Principal: AWS: !Sub arn:aws:iam::${DeployAccountId}:root Action: kms:* Resource: '*' - Sid: 'Allow access for Key Administrators' Effect: Allow Principal: AWS: - !GetAtt SpaCodeBuildRole.Arn - !GetAtt SpaPipelineRole.Arn Action: - kms:Create* - kms:Describe* - kms:Enable* - kms:List* - kms:Put* - kms:Update* - kms:Revoke* - kms:Disable* - kms:Get* - kms:Delete* - kms:TagResource - kms:UntagResource Resource: '*' - Sid: 'Allow use of the key' Effect: Allow Principal: AWS: - !Sub arn:aws:iam::${SourceAccountId}:root - !GetAtt SpaCodeBuildRole.Arn - !GetAtt SpaPipelineRole.Arn Action: - kms:Encrypt - kms:Decrypt - kms:ReEncrypt* - kms:GenerateDataKey* - kms:DescribeKey Resource: '*' - Sid: 'Allow attachment of persistent resources' Effect: Allow Principal: AWS: - !Sub arn:aws:iam::${SourceAccountId}:root - !GetAtt SpaCodeBuildRole.Arn - !GetAtt SpaPipelineRole.Arn Action: - kms:CreateGrant - kms:ListGrants - kms:RevokeGrant Resource: '*' Condition: Bool: kms:GrantIsForAWSResource: true #アーティファクト用S3バケットを作成 SpaSourceS3: Type: AWS::S3::Bucket Properties: BucketName: !Sub ${Env}-auto-deploy-${SpaName} BucketEncryption: ServerSideEncryptionConfiguration: - ServerSideEncryptionByDefault: SSEAlgorithm: aws:kms KMSMasterKeyID: !Ref SpaKmsKey SpaSourceS3BacketPolicy: Type: AWS::S3::BucketPolicy Properties: Bucket: !Ref SpaSourceS3 PolicyDocument: Version: '2012-10-17' Id: SSEAndSSLPolicy Statement: - Sid: CrossAccountS3GetPutPolicy Effect: Allow Principal: AWS: !Sub arn:aws:iam::${SourceAccountId}:root Action: - s3:Get* - s3:Put* Resource: !Sub arn:aws:s3:::${Env}-auto-deploy-${SpaName}/* - Sid: CrossAccountS3ListPolicy Effect: Allow Principal: AWS: !Sub arn:aws:iam::${SourceAccountId}:root Action: s3:* Resource: !Sub arn:aws:s3:::${Env}-auto-deploy-${SpaName}/* - Sid: CodePipeline Effect: Allow Principal: AWS: !GetAtt SpaPipelineRole.Arn Action: s3:* Resource: !Sub arn:aws:s3:::${Env}-auto-deploy-${SpaName}/* #SPAをS3にデプロイするCodebuildプロジェクトを作成 SpaCodebuild: Type: AWS::CodeBuild::Project Properties: Artifacts: Type: CODEPIPELINE Description: 'Build a SPA across accounts' EncryptionKey: !GetAtt SpaKmsKey.Arn Environment: Type: LINUX_CONTAINER ComputeType: BUILD_GENERAL1_SMALL Image: 'aws/codebuild/nodejs:8.11.0' Name: !Sub ${Env}-auto-deploy-${SpaName} ServiceRole: !GetAtt SpaCodeBuildRole.Arn Source: Type: CODEPIPELINE BuildSpec: !Sub codebuild/buildspec.${Env}.yml #Codecommitからソースを読み込んでCodebuildに受け渡すCodePipelineを作成 SpaPipeline: Type: AWS::CodePipeline::Pipeline Properties: Name: !Sub ${Env}-auto-deploy-${SpaName} ArtifactStore: Type: S3 Location: !Sub ${Env}-auto-deploy-${SpaName} EncryptionKey: Id: !GetAtt SpaKmsKey.Arn Type: KMS RoleArn: !GetAtt SpaPipelineRole.Arn Stages: - Name: Source Actions: - ActionTypeId: Category: Source Owner: AWS Provider: CodeCommit Version: 1 Name: Source Configuration: BranchName: !Sub ${BranchName} RepositoryName: !Sub ${SpaName} OutputArtifacts: - Name: SpaSourceCode RoleArn: !Sub arn:aws:iam::${SourceAccountId}:role/${Env}-auto-deploy-${SpaName}-for-codecommit RunOrder: 1 - Name: build Actions: - ActionTypeId: Category: Build Owner: AWS Provider: CodeBuild Version: 1 Name: Build Configuration: ProjectName: !Ref SpaCodebuild InputArtifacts: - Name: SpaSourceCode RunOrder: 1
ソースアカウントにAssumeRoleするためのIAMRoleを作成するテンプレート
こちらは開発アカウントで実行します。 ステージングと本番アカウントからAssumeroleするためのIAMロールを作成するのでパラメーターのEnvにはデプロイ先の環境名を入力しましょう。
- Env:デプロイ先の環境名(staging=stg、production=prd)
- DeployAccountId:デプロイ先のAWSアカウントID
- SourceAccountId:CodeCommitのソースが置いてあるアカウントID
- SpaName:CodeCommitのリポジトリ名
AWSTemplateFormatVersion: '2010-09-09' Description: 'Create resources for automatic deployment of SPA' Parameters: Env: Type: String Default: stg AllowedValues: - stg - prd Description: 'Set Enter Environment for SPA' DeployAccountId: Type: String Default: 123456789012 AllowedValues: - 123456789012 - 098765432109 Description: 'Set Enter Account ID to deploy SPA' SourceAccountId: Type: String Default: 678901234567 Description: 'Set Enter Account ID for SPA source' SpaName: Type: String Default: my-app AllowedValues: - my-app - my-app2 Description: 'Set Enter SPA name' Resources: # Create an IAM role to access codecommit PipelineRole: Type: AWS::IAM::Role Properties: AssumeRolePolicyDocument: Version: '2012-10-17' Statement: - Effect: Allow Principal: AWS: !Sub arn:aws:iam::${DeployAccountId}:root Action: sts:AssumeRole Policies: - PolicyName: !Sub ${Env}-auto-deploy-${SpaName}-for-codecommit PolicyDocument: Version: '2012-10-17' Statement: - Effect: Allow Action: - s3:* Resource: - !Sub arn:aws:s3:::${Env}-auto-deploy-${SpaName} - !Sub arn:aws:s3:::${Env}-auto-deploy-${SpaName}/* - Effect: Allow Action: - kms:DescribeKey - kms:GenerateDataKey* - kms:Encrypt - kms:ReEncrypt* - kms:Decrypt Resource: - '*' - Effect: Allow Action: - codecommit:GetBranch - codecommit:GetCommit - codecommit:UploadArchive - codecommit:GetUploadArchiveStatus - codecommit:CancelUploadArchive Resource: - !Sub arn:aws:codecommit:ap-northeast-1:${SourceAccountId}:${SpaName} RoleName: !Sub ${Env}-auto-deploy-${SpaName}-for-codecommit
ソースアカウントにpipeline,buildプロジェクトを作成するテンプレート
最後に開発アカウントにPipalineを作成します。 こちらはアカウントは跨がないのでシンプルなテンプレートになります。
AWSTemplateFormatVersion: '2010-09-09' Description: 'Create resources for automatic deployment of SPA' Parameters: Env: Type: String Default: itg Description: 'Set Enter Environment for SPA' DeployAccountId: Type: String Default: 678901234567 AllowedValues: - 678901234567 Description: 'Set Enter Account ID to deploy SPA' SpaName: Type: String Default: my-app AllowedValues: - my-app - my-app2 Description: 'Set Enter Repository name' BranchName: Type: String Default: integration AllowedValues: - integration Description: 'Set Enter branch name' Resources: # create IAM role for pipeline SpaPipelineRole: Type: AWS::IAM::Role Properties: AssumeRolePolicyDocument: Version: '2012-10-17' Statement: - Effect: Allow Principal: Service: codepipeline.amazonaws.com Action: sts:AssumeRole Policies: - PolicyDocument: Version: '2012-10-17' Statement: - Effect: Allow Action: - s3:* Resource: - !Sub arn:aws:s3:::${Env}-auto-deploy-${SpaName} - !Sub arn:aws:s3:::${Env}-auto-deploy-${SpaName}/* - Effect: Allow Action: - codebuild:StartBuild - codebuild:BatchGetBuilds Resource: - '*' PolicyName: !Sub ${Env}-auto-deploy-${SpaName}-for-pipeline RoleName: !Sub ${Env}-auto-deploy-${SpaName}-for-pipeline # create IAM role for codebuild project SpaCodeBuildRole: Type: AWS::IAM::Role Properties: AssumeRolePolicyDocument: Version: '2012-10-17' Statement: - Effect: Allow Principal: Service: codebuild.amazonaws.com Action: sts:AssumeRole Policies: - PolicyDocument: Version: '2012-10-17' Statement: - Effect: Allow Action: - logs:* - cloudfront:CreateInvalidation Resource: '*' - Effect: Allow Action: - s3:* Resource: '*' - PolicyName: !Sub ${Env}-auto-deploy-${SpaName}-for-codebuild RoleName: !Sub ${Env}-auto-deploy-${SpaName}-for-codebuild # Create artifact S3 bucket SpaSourceS3: Type: AWS::S3::Bucket Properties: BucketName: !Sub ${Env}-auto-deploy-${SpaName} SpaCodebuild: Type: AWS::CodeBuild::Project Properties: Artifacts: Type: CODEPIPELINE Description: 'Build a SPA' Environment: Type: LINUX_CONTAINER ComputeType: BUILD_GENERAL1_SMALL Image: 'aws/codebuild/nodejs:8.11.0' Name: !Sub ${Env}-auto-deploy-${SpaName} ServiceRole: !GetAtt SpaCodeBuildRole.Arn Source: Type: CODEPIPELINE buildspec: !Sub codebuild/buildspec.${Env}.yml SpaPipaline: Type: AWS::CodePipeline::Pipeline Properties: ArtifactStore: Type: S3 Location: !Sub ${Env}-auto-deploy-${SpaName} Name: !Sub ${Env}-auto-deploy-{SpaName} RoleArn: !GetAtt SpaPipelineRole.Arn Stages: Name: 'Source' Actions: - ActionsTypeId: Category: Source Owner: AWS Provider: CodeCommit Version: 1 Configuration: BranchName: !Sub ${BranchName} RepositoryName: !Sub ${SpaName} OutputArtifacts: - Name: SpaSourceCode RunOrder: 1 Stages: Name: build Actions: - ActionsTypeId: Category: Build Owner: AWS Provider: CodeBuild Version: 1 Configuration: ProjectName: !Ref SpaCodebuild InputArtifacts: - Name: SpaSourceCode RunOrder: 1
注意点
CFnでIAMロールを作成する場合、確認画面で CAPABILITY
項目でチェックを入れる必要があります。
チェックを入れないとスタック作成時に Requires capabilities : [CAPABILITY_NAMED_IAM]
のエラーが発生してIAMロールを作成できません。
CLIで実行する場合は
aws cloudformation create-stack
もしくは aws cloudformation update-stack
実行時のオプション --capabilities
に CAPABILITY_NAMED_IAM を指定します。